#ifndef AFCORE_DATABASE_DATABASE_DATEBASE_WORKERPOOL_
#define AFCORE_DATABASE_DATABASE_DATEBASE_WORKERPOOL_

#include <array>
#include <string>
#include <vector>

#include "define.h"
#include "database_fwd.h"
#include "stringformat.h"

namespace afcore {

template<typename T>
class CPcQueue;

/// @brief  数据库
namespace database {

class CSqlOperation;
struct SMysqlConnectionInfo;

template <typename T>
class CDatabaseWorkerPool {
  enum EInternalIndex {
    kInternalIndex_Async,
    kInternalIndex_Synch,
    kInternalIndex_Size,
  };
public:
  CDatabaseWorkerPool();
  ~CDatabaseWorkerPool();

  void SetConnectionInfo(const std::string& conn_info, const uint8_t async_threads, const uint8_t synch_threads);

  uint32_t Open();

  void Close();

  bool PrepareStatements();

  inline const SMysqlConnectionInfo* GetConnectionInfo() const {
    return conn_info_.get();
  }

  void Execute(const char* sql);

  template<typename Format, typename... Args>
  void PExecute(Format&& sql, Args&&... args)
  {
    if (IsFormatEmptyOrNull(sql))
      return;

    Execute(StringFormat(std::forward<Format>(sql), std::forward<Args>(args)...).c_str());
  }

  void Execute(CPreparedStatement<T>* stmt);

  void DirectExecute(const char* sql);

  template<typename Format, typename... Args>
  void DirectPExecute(Format&& sql, Args&&... args)
  {
    if (IsFormatEmptyOrNull(sql))
      return;

    DirectExecute(StringFormat(std::forward<Format>(sql), std::forward<Args>(args)...).c_str());
  }

  void DirectExecute(CPreparedStatement<T>* stmt);

  RQueryResultSptr Query(const char* sql, T* connection = nullptr);

  template<typename Format, typename... Args>
  RQueryResultSptr PQuery(Format&& sql, T* conn, Args&&... args)
  {
    if (IsFormatEmptyOrNull(sql))
      return RQueryResultSptr(nullptr);

    return Query(StringFormat(std::forward<Format>(sql), std::forward<Args>(args)...).c_str(), conn);
  }

  template<typename Format, typename... Args>
  RQueryResultSptr PQuery(Format&& sql, Args&&... args)
  {
    if (IsFormatEmptyOrNull(sql))
      return RQueryResultSptr(nullptr);

    return Query(StringFormat(std::forward<Format>(sql), std::forward<Args>(args)...).c_str());
  }

  RPreparedQueryResultSptr Query(CPreparedStatement<T>* stmt);

  CQueryCallback AsyncQuery(const char* sql);

  CQueryCallback AsyncQuery(CPreparedStatement<T>* stmt);

  RQueryHolderFutrue DelayQueryHolder(CSqlQueryHolder<T>* holder);

  RSqlTransaction<T> BeginTransaction();

  void CommitTransaction(RSqlTransaction<T> transaction);

  void DirectCommitTransaction(RSqlTransaction<T>& transaction);

  void ExecuteOrAppend(RSqlTransaction<T>& trans, const char* sql);

  void ExecuteOrAppend(RSqlTransaction<T>& trans, CPreparedStatement<T>* stmt);

  using RPreparedStatementIndex = typename T::RStatements;

  CPreparedStatement<T>* GetPreparedStatement(RPreparedStatementIndex index);

  void EscapeString(std::string& str);

  void KeepAlive();

private:
  uint32_t OpenConnections(EInternalIndex type, uint8_t conns_num);

  unsigned long EscapeString(char *to, const char *from, unsigned long length);

  void Enqueue(CSqlOperation* op);

  T* GetFreeConnection();

  const char* GetDatabaseName() const;

private:
  std::unique_ptr<CPcQueue<CSqlOperation*>> q_;
  std::array<std::vector<std::unique_ptr<T>>, kInternalIndex_Size> conns_;
  std::unique_ptr<SMysqlConnectionInfo> conn_info_;
  std::vector<uint8_t> prepared_stmt_size_;
  uint8_t async_threads_{0};
  uint8_t synch_threads_{0};
};

} // !namespace database

} // !namespace afcore

#endif //! AFCORE_DATABASE_DATABASE_DATEBASE_WORKERPOOL_